
var obj = {
  name: '张三'
}

function show(...args) {
  console.log(args);
  console.log(this.name);
}

// call方法执行原理 参数用剩余参数...，所有参数都在args数组中
Function.prototype.myCall = function (obj, ...args) {
  // 防止对象中的属性与临时属性重名
  let fn = Symbol(1)
  // 存储调用call方法的函数,挂载到传入的obj并作为属性
  obj[fn] = this
  // 调用添加的函数 参数用扩展运算符...展开数组
  obj[fn](...args)
  // 删除挂载的函数
  delete obj[fn]
}
show.myCall(obj, 1, 2)
// show.call(obj, 1, 2)

// apply原理
Function.prototype.myApply = function (obj, arr) {
  // 判断传入的参数是否为数组  （改正：apply方法可以传数组或者类数组对象）
  if (arr && !(arr instanceof Array)) {
    throw ('apply方法只接受数组类型的参数')
  }
  let fn = Symbol(1)
  // 存储调用apply方法的函数,挂载到传入的obj并作为属性
  obj[fn] = this
  // 调用添加的函数 参数用扩展运算符...展开数组
  obj[fn](...arr)
  // 删除挂载的函数
  delete obj[fn]
}
show.myApply(obj, [1, 2])

// bind原理
// bind方法有两次传参，第一次调用bind方法时传参，第二次调用bind方法的返回函数时传参
Function.prototype.myBind = function (obj, ...args1) {
  return (...args2) => {
    let fn = Symbol(1)
    obj[fn] = this
    // 调用函数时拼接两次传入的参数
    obj[fn](...(args1.concat(args2)))
    delete obj[fn]
  }
}
show.myBind(obj, 1, 2)(3, 4)